﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.IO;

namespace RawLevelFormat
{
    public class RawLevel
    {
        public enum ElementType
        {
            BACKGROUND,
            FOREGROUND,
            OBSTACLE,
            PORTALABLE,
            SCENERY,
            BURNABLE
        }

        public struct Element
        {
            public UInt16 textureId;
            public ElementType type;
            public Vector2 position;
            public string name;
        }

        public struct PointMark
        {
            public string name;
            public Vector2 position;
        }

        private List<string> textures;
        private List<Element> elements;
        private List<PointMark> pointMarks;

        public RawLevel()
        {
            textures = new List<string>();
            elements = new List<Element>();
            pointMarks = new List<PointMark>();
        }

        public static RawLevel fromBytes(byte[] data)
        {
            RawLevel rl = new RawLevel();

            BinaryReader br = new BinaryReader(new MemoryStream(data));

            // reading textures
            UInt16 numTextures = br.ReadUInt16();
            for (UInt16 i = 0; i < numTextures; i++)
            {
                string texture = br.ReadString();
                rl.addTexture(texture);
            }
            
            // reading elements
            int numElements = br.ReadInt32();
            for (int i = 0; i < numElements; i++)
            {
                Element element;
                element.textureId = br.ReadUInt16();
                element.type = (ElementType)br.ReadUInt16();
                float x = br.ReadSingle();
                float y = br.ReadSingle();
                element.position = new Vector2(x, y);

                element.name = "";
                bool hasName = br.ReadBoolean();
                if (hasName)
                    element.name = br.ReadString();

                rl.addElement(element);
            }

            // reading point marks
            int numPointMarks = br.ReadInt32();
            for (int i = 0; i < numPointMarks; i++)
            {
                PointMark pm;
                pm.name = br.ReadString();
                float x = br.ReadSingle();
                float y = br.ReadSingle();
                pm.position = new Vector2(x, y);
                rl.addPointMark(pm);
            }

            return rl;
        }

        public byte[] toBytes()
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter output = new BinaryWriter(ms);

            // writes textures name
            output.Write((UInt16)Textures.Count);
            foreach (string texture in Textures)
                output.Write(texture);

            // writes elements
            output.Write(Elements.Count);
            foreach (Element element in Elements)
            {
                output.Write((UInt16)element.textureId);
                output.Write((UInt16)element.type);
                output.Write(element.position.X);
                output.Write(element.position.Y);

                if (element.name == null || element.name == "")
                    output.Write(false); // no name provided

                else
                {
                    output.Write(true);
                    output.Write(element.name);
                }
            }

            // writes point marks
            output.Write(PointMarks.Count);
            foreach (PointMark pointMark in PointMarks)
            {
                output.Write(pointMark.name);
                output.Write(pointMark.position.X);
                output.Write(pointMark.position.Y);
            }

            ms.Position = 0;

            int streamLength = (int)ms.Length;
            byte[] bytes = new byte[streamLength];
            ms.Read(bytes, 0, streamLength);

            output.Close();

            return bytes;
        }

        public UInt16 addTexture(string texture)
        {
            int id = textures.IndexOf(texture);
            if (id == -1)
            {
                textures.Add(texture);
                id = textures.Count - 1;
            }
            return (UInt16)id;
        }

        public void addElement(Element element)
        {
            elements.Add(element);
        }

        public void addPointMark(PointMark pointMark)
        {
            pointMarks.Add(pointMark);
        }

        public List<string> Textures
        {
            get { return textures; }
        }

        public List<Element> Elements
        {
            get { return elements; }
        }

        public List<PointMark> PointMarks
        {
            get { return pointMarks; }
        }
    }
}
